package server;

import java.util.Collections;
import java.util.List;
import java.util.Vector;

import network.message.IMessage;
import network.message.IMessage.MessageType;
import network.message.Message;

import shared.PlayerPosition;
import shared.PlayerState;
import deck.Card;
import deck.Deck;
import deck.OutOfCardsException;
import deck.Suit;

public class SetbackMatch {
	private IPlayer north, south, east, west;
	private Integer northSouthScore, eastWestScore;
	private Suit currentTrump, leadSuit;
	
	private SetbackServer server;
	
	private Deck deck;
	
	private PlayerPosition activePlayer, leadPlayer;
	private PlayerPosition winningBidder;
	private int winningBid;
	private PlayerPosition dealer;
	
	private static List<Integer> allBids = new Vector<Integer>();
	static {
		allBids.add(0);
		allBids.add(2);
		allBids.add(3);
		allBids.add(4);
		allBids.add(5);
	}
	
	public SetbackMatch(SetbackServer server) {
		this.server = server;
		deck = new Deck();
		deck.shuffle();
	}

	
	public void addPlayer(IPlayer player) {
		
		switch (player.getCurrentState().getPlayerPosition()) {
		case EAST:
			if (east == null || east instanceof RobotPlayer) {
				east = player;
			}
			break;
		case WEST:
			if (west == null || west instanceof RobotPlayer) {
				west = player;
			}
			break;
		case NORTH:
			if (north == null || north instanceof RobotPlayer) {
				north = player;
				activePlayer = PlayerPosition.NORTH;
			}
			break;
		case SOUTH:
			if (south == null || south instanceof RobotPlayer) {
				south = player;
				activePlayer = PlayerPosition.SOUTH;
			}
			break;
		}

		if (! (player instanceof RobotPlayer)) {
			if (east != null) {
				IMessage eastPlayerStateMessage = new Message(MessageType.PLAYER_INFO, east.getCurrentState());
				server.sendMessage(eastPlayerStateMessage, player.getCurrentState().getPlayerPosition());
			}
			if (west != null) {
				IMessage westPlayerStateMessage = new Message(MessageType.PLAYER_INFO, west.getCurrentState());
				server.sendMessage(westPlayerStateMessage, player.getCurrentState().getPlayerPosition());
			}
			if (north != null) {
				IMessage northPlayerStateMessage = new Message(MessageType.PLAYER_INFO, north.getCurrentState());
				server.sendMessage(northPlayerStateMessage, player.getCurrentState().getPlayerPosition());
			}
			if (south != null) {
				IMessage southPlayerStateMessage = new Message(MessageType.PLAYER_INFO, south.getCurrentState());
				server.sendMessage(southPlayerStateMessage, player.getCurrentState().getPlayerPosition());
			}
			// test
			Vector<Integer> i = new Vector<Integer>();
			i.add(0);
			i.add(3);
			i.add(4);
			i.add(5);
			IMessage bidRequest = new Message(MessageType.SUIT_MESSAGE, null);
			
//			server.sendMessage(bidRequest, player.getCurrentState().getPlayerPosition());
		}
		
		
		

		
	}
	
	public void dealFinished() {
		// rotate dealer and clear everything else
		dealer = playerOnLeft(dealer);
		winningBid = 0;
		winningBidder = null;
		currentTrump = null;
		east.getCurrentState().clearHand();
		west.getCurrentState().clearHand();
		north.getCurrentState().clearHand();
		south.getCurrentState().clearHand();
		
		deck.replenish();
		
		activePlayer = dealer;
		
	}
	
	public IPlayer getPlayerAtPosition(PlayerPosition pos) {
		switch (pos) {
		case NORTH:
			return north;
		case SOUTH:
			return south;
		case EAST:
			return east;
		case WEST:
			return west;
		}
		return null;
	}
	
	
	public Suit getCurrentTrump() {
		return currentTrump;
	}
	
	public void nextDealer() {
		dealer = playerOnLeft(dealer);
	}
	
	public void nextActivePlayer() {
		activePlayer = playerOnLeft(activePlayer);
	}
	
	public void setActivePlayer (PlayerPosition p) {
		activePlayer = p;
	}
	public PlayerPosition getActivePlayer() {
		return activePlayer;
	}
	public PlayerPosition getDealer() {
		return dealer;
	}
	private PlayerPosition playerOnLeft(PlayerPosition p) {
		PlayerPosition r;
		if (p == PlayerPosition.NORTH) {
			r = PlayerPosition.EAST;
		} else if (p == PlayerPosition.EAST) {
			r = PlayerPosition.SOUTH;
		} else if (p == PlayerPosition.SOUTH) {
			r = PlayerPosition.WEST;
		} else {
			r = PlayerPosition.NORTH;
		}
		return r;
		
	}
	
	private void sendActivePlayerUpdateToAllPlayers() {
		
		PlayerState s = getPlayerAtPosition(activePlayer).getCurrentState().copy();
		
		// send update message back to active player
		server.sendMessage(new Message(MessageType.PLAYER_INFO, s), activePlayer);
		
		// must clear out hand for other players so the update doesnt contain hand info
		for (Card c : s.getHand()) {
			c.setValue(null);
			c.setSuit(Suit.RED);
		}

		PlayerPosition player = playerOnLeft(activePlayer);
		for (int i = 0; i < 3; i++) {
			server.sendMessage(new Message(MessageType.PLAYER_INFO, s), player);
			player = playerOnLeft(player);
		}
	}
	
	public void bidPlaced(Integer bid) {
		getPlayerAtPosition(activePlayer).getCurrentState().setBid(bid);
		sendActivePlayerUpdateToAllPlayers();
		if (bid != 0) {
			winningBidder = activePlayer;
			winningBid = bid;
		}
		// push player update to each client with bidding player's state
		
		sendActivePlayerUpdateToAllPlayers();
		
		if (activePlayer == dealer) {
			// done with bidding round -> push a trump message to winning bidder && a winning bid info message
			
			// send bid info to all clients notifying them of the winning bid number (no response expected)
			IMessage bidInfo = new Message(MessageType.BID_INFO, winningBid);
			server.sendMessage(bidInfo);
			
			// send a trump request to the winning bidder to request a suit (Suit Message response expected)
			IMessage trumpRequest = new Message(MessageType.SUIT_MESSAGE, null);
			server.sendMessage(trumpRequest, winningBidder);
			
			// active player = winning bidder
			activePlayer = winningBidder;
		} else {
			nextActivePlayer();
			// Determine valid bids for this player
			List<Integer> validBids = new Vector<Integer>();
			validBids.addAll(allBids);
			if (winningBid >= 2) {
				for (int i = 2; i <= winningBid; i++) {
					validBids.remove(i);
				}
			} else if (activePlayer == dealer) {
				validBids.remove(0);
			}
			
			// send bid request to active player (expect a bid message response and return to this method)
			IMessage bidRequest = new Message(MessageType.BID_MESSAGE, validBids);
			server.sendMessage(bidRequest, activePlayer);
		}
		
		
	}
	public void cardPlayed(Card c) {
		getPlayerAtPosition(activePlayer).getCurrentState().setPlayedCard(c);
		getPlayerAtPosition(activePlayer).getCurrentState().getHand().remove(c);
		sendActivePlayerUpdateToAllPlayers();
		if (activePlayer == leadPlayer) {
			leadSuit = c.getSuit();
		} 
		if (playerOnLeft(activePlayer) == leadPlayer) {
			
			// done with one card playing round -> determine winner of trick
			List<Card> playedCards = new Vector<Card>();
			playedCards.add(east.getCurrentState().getPlayedCard());
			playedCards.add(north.getCurrentState().getPlayedCard());
			playedCards.add(west.getCurrentState().getPlayedCard());
			playedCards.add(south.getCurrentState().getPlayedCard());
			
			List<Card> playedTrump = getCardsOfSuit(playedCards, currentTrump);
			Card winningCard = null;
			if (playedTrump.size() == 0) {
				// no one played a trump -> highest of lead suit wins
				Collections.sort(playedCards);
				boolean stillSearching = true;
				for (int i = playedCards.size() - 1; i >= 0 && stillSearching ; i++) {
					if (playedCards.get(i).getSuit() == leadSuit) {
						winningCard = playedCards.get(i);
						stillSearching = false;
					}
				}
				// should never have winningCard = null here
			} else {
				// someone played a trump -> highest trump wins
				Collections.sort(playedCards);
				boolean stillSearching = true;
				for (int i = playedCards.size() - 1; i >= 0 && stillSearching ; i++) {
					if (playedCards.get(i).getSuit() == currentTrump) {
						winningCard = playedCards.get(i);
						stillSearching = false;
					}
				}
				// should never have winningCard = null here
			}
			
			IPlayer winningPlayer = null;
			
			if (east.getCurrentState().getPlayedCard() == winningCard) {
				winningPlayer = east;
			} else if (west.getCurrentState().getPlayedCard() == winningCard) {
				winningPlayer = west;
			} else if (north.getCurrentState().getPlayedCard() == winningCard) {
				winningPlayer = north;
			} else if (south.getCurrentState().getPlayedCard() == winningCard) {
				winningPlayer = south;
			}else {
				// something bad happened
			}
			
			winningPlayer.getCurrentState().addToOwnedCards(playedCards);
			
			east.getCurrentState().setPlayedCard(null);
			west.getCurrentState().setPlayedCard(null);
			north.getCurrentState().setPlayedCard(null);
			south.getCurrentState().setPlayedCard(null);
			
			// that winner is new active player
			activePlayer = winningPlayer.getCurrentState().getPlayerPosition();
			// send them a play request if the hand size is > 0
			if (winningPlayer.getCurrentState().getHand().size() > 0) {
				// send play message
				IMessage playMessage = new Message(MessageType.PLAY_MESSAGE, winningPlayer.getCurrentState().getHand());
				server.sendMessage(playMessage, activePlayer);
			} else {
				// done with this deal!  Determine deal score!
				determineScore();
			}
			// clear leadSuit
			leadSuit = null;
		} else {
			nextActivePlayer();
			// determine valid cards for this next player using trump and leadSuit
			List<Card> playerHand = getPlayerAtPosition(activePlayer).getCurrentState().getHand();
			List<Card> validCards = new Vector<Card>();
			validCards.addAll(getCardsOfSuit(playerHand, leadSuit));
			if (validCards.size() == 0) {
				// active player has no lead suit -> whole hand is valid
				validCards = playerHand;
			} else {
				// active player has lead suit -> lead suit or trump is valid
				validCards.addAll(getCardsOfSuit(playerHand, currentTrump));
			}

			
			// send a play request to that player
			IMessage playRequest = new Message(MessageType.PLAY_MESSAGE, validCards);
			server.sendMessage(playRequest);
		}
	}
	
	private void determineScore() {
		List<Card> northSouthCards = new Vector<Card>();
		List<Card> eastWestCards = new Vector<Card>();
		List<Card> allCards = new Vector<Card>();
		
		northSouthCards.addAll(north.getCurrentState().getOwnedCards());
		northSouthCards.addAll(south.getCurrentState().getOwnedCards());
		
		eastWestCards.addAll(east.getCurrentState().getOwnedCards());
		eastWestCards.addAll(west.getCurrentState().getOwnedCards());
		
		allCards.addAll(northSouthCards);
		allCards.addAll(eastWestCards);
		
		Card highCard = UtilScoring.determineHigh(allCards, currentTrump);
		Card lowCard = UtilScoring.determineLow(allCards, currentTrump);
		Card jack = UtilScoring.determineJack(allCards, currentTrump);
		PlayerPosition game = UtilScoring.determineGame(northSouthCards, eastWestCards);
		
		int nsScore = 0;
		int ewScore = 0;
		
		if (northSouthCards.contains(highCard)) {
			nsScore +=1;
		} else {
			ewScore += 1;
		}
		
		if (northSouthCards.contains(lowCard)) {
			nsScore += 1;
		} else {
			ewScore += 1;
		}
		
		if (jack != null) {
			if (northSouthCards.contains(jack)) {
				nsScore += 1;
			} else {
				ewScore += 1;
			}
		}
		
		if (game != null) {
			if (game == PlayerPosition.NORTH) {
				nsScore += 1;
			} else {
				ewScore += 1;
			}
		}
		
		if (winningBidder == PlayerPosition.NORTH || winningBidder == PlayerPosition.SOUTH) {
			if (nsScore < winningBid && nsScore != 4) {
				nsScore = -winningBid;
			} else if (winningBid == 5 && nsScore == 4) {
				nsScore = 5;
			}
		} else {
			if (ewScore < winningBid && ewScore != 4) {
				ewScore = -winningBid;
			} else if (winningBid == 5 && ewScore == 4) {
				ewScore = 5;
			}
		}
		
		
		// send a round update message?
		
		northSouthScore += nsScore;
		eastWestScore += ewScore;
		
		north.getCurrentState().setScore(northSouthScore);
		south.getCurrentState().setScore(northSouthScore);
		east.getCurrentState().setScore(eastWestScore);
		west.getCurrentState().setScore(eastWestScore);
		
		// determine if winning team has achieved the winning score
		
		if (northSouthScore >= 21 && (winningBidder == PlayerPosition.NORTH || winningBidder == PlayerPosition.SOUTH || eastWestScore < 10)) {
			// north south team wins!  match is over
		}
		else if (eastWestScore >= 21 && (winningBidder == PlayerPosition.EAST || winningBidder == PlayerPosition.WEST || northSouthScore < 10)) {
			// east west team wins! match is over
		} else {
			// match is not over yet
			dealFinished();
			// send a deal message to the dealer
			IMessage dealRequest = new Message(MessageType.DEAL_MESSAGE, null);
			server.sendMessage(dealRequest, dealer);
		}
		
	}
	
	private List<Card> getCardsOfSuit(List<Card> hand, Suit suit) {
		List<Card> suitedCards = new Vector<Card>();
		for (Card c : hand) {
			if (c.getSuit() == suit) {
				suitedCards.add(c);
			}
		}
		return suitedCards;
	}
	public void cardsTraded(List<Card> cards) throws OutOfCardsException {
		getPlayerAtPosition(activePlayer).getCurrentState().getHand().removeAll(cards);
		//TODO this isn't telling other clients the amount of cards he traded in (send player update here?)
		getPlayerAtPosition(activePlayer).getCurrentState().getHand().addAll(deck.drawCardsFromDeck(cards.size()));
		if (activePlayer == dealer) {
			// done with card trading round -> push a play request to winning bidder
			activePlayer = winningBidder;
			IMessage playRequest = new Message(MessageType.PLAY_MESSAGE, getPlayerAtPosition(activePlayer).getCurrentState().getHand());
			server.sendMessage(playRequest, activePlayer);
			// active player = winning bidder
		} else {
			// not done with card trading round -> send trade request to next player
			nextActivePlayer();
			List<Card> validTradeins = new Vector<Card>();
			for (Card c : getPlayerAtPosition(activePlayer).getCurrentState().getHand()) {
				if (c.getSuit() == currentTrump) {
					validTradeins.add(c);
				}
			}
			IMessage tradeRequest = new Message(MessageType.TRADE_MESSAGE, validTradeins);
			server.sendMessage(tradeRequest, activePlayer);
			
 		}
	}
	public void setCurrentTrump(Suit currentTrump) {
		this.currentTrump = currentTrump;
		// push a trump info message to each client (no response expected)
		IMessage message = new Message(MessageType.TRUMP_INFO, currentTrump);
		server.sendMessage(message);
		// send a trade request to the left of the dealer
		activePlayer = playerOnLeft(dealer);
		List<Card> validTradeins = new Vector<Card>();
		for (Card c : getPlayerAtPosition(activePlayer).getCurrentState().getHand()) {
			if (c.getSuit() != currentTrump) {
				validTradeins.add(c);
			}
		}
		IMessage tradeRequest = new Message(MessageType.TRADE_MESSAGE, validTradeins);
		server.sendMessage(tradeRequest, activePlayer);
	}
	public void dealCards() throws OutOfCardsException {
		north.getCurrentState().clearHand();
		south.getCurrentState().clearHand();
		east.getCurrentState().clearHand();
		west.getCurrentState().clearHand();
		
		PlayerPosition dealTo = playerOnLeft(dealer);
		for (int i = 0; i < 8; i++) {
			getPlayerAtPosition(dealTo).getCurrentState().addToHand(deck.drawCardsFromDeck(3));
			dealTo = playerOnLeft(dealTo);
		}
		
		// send bid request to player on left of dealer
		IMessage bidRequest = new Message(MessageType.BID_MESSAGE, allBids);
		server.sendMessage(bidRequest, playerOnLeft(dealer));
	}
	public void setLeadSuit(Suit leadSuit) {
		this.leadSuit = leadSuit;
	}

	public Suit getLeadSuit() {
		return leadSuit;
	}

	public void setLeadPlayer(PlayerPosition leadPlayer) {
		this.leadPlayer = leadPlayer;
	}

	public PlayerPosition getLeadPlayer() {
		return leadPlayer;
	}
}
